#include "hardcodedSkin.h"

#include <spri/data.h>
#include <spri/assets.h>

#include <imag/data.h>
#include <imag/resources.h>
#include <imag/assets.h>

#include <util/indexMap.h>

#include <foun/debug.h>

#include <string.h>

/*
 * Internal Constants
 */
static const u32 SHAPE_TEXTURE_WIDTH = 32;
static const u32 SHAPE_TEXTURE_HEIGHT = 32;
static const u32 SHAPE_TEXTURE_SIZE = SHAPE_TEXTURE_WIDTH * SHAPE_TEXTURE_HEIGHT * sizeof(lct::imag::TexelRGBAData);
static const u32 CIRCLE_RADIUS = 16;

static const u32 JOINT_TEXTURE_WIDTH = 32;
static const u32 JOINT_TEXTURE_HEIGHT = 32;
static const u32 JOINT_TEXTURE_SIZE = JOINT_TEXTURE_WIDTH * JOINT_TEXTURE_HEIGHT * sizeof(lct::imag::TexelRGBAData);

/*
 * Public Static
 */
lct::spri::SkinAsset* HardcodedSkin::BuildAsset(lct::foun::Allocator* pAllocator)
{
	const u32 TEXTURE_TABLE_COUNT = 2;

	lct::imag::TextureTableAsset* pTextureTableAssetArray = pAllocator->AllocTypeArray<lct::imag::TextureTableAsset>(TEXTURE_TABLE_COUNT);
	{
		u32 textureTableIndex = 0;
		{
			const u32 TEXTURE_COUNT = 2;
			const u32 TEXTURE_TABLE_BINARY_SIZE = SHAPE_TEXTURE_SIZE * TEXTURE_COUNT;
			void* pTextureTableBinary = pAllocator->Alloc(TEXTURE_TABLE_BINARY_SIZE, 4);
			void* pMemory = pTextureTableBinary;
			{
				pMemory = WriteCircleTexture(SHAPE_TEXTURE_WIDTH, SHAPE_TEXTURE_HEIGHT, CIRCLE_RADIUS, pMemory);
			}
			{
				pMemory = WriteTriangleTexture(SHAPE_TEXTURE_WIDTH, SHAPE_TEXTURE_HEIGHT, pMemory);
			}
			LCT_ASSERT((u32)pMemory == ((u32)pTextureTableBinary + TEXTURE_TABLE_BINARY_SIZE));

			lct::imag::TextureTableData* pTextureTableData = pAllocator->AllocType<lct::imag::TextureTableData>();
			strcpy(pTextureTableData->name, "shape");
			pTextureTableData->width = SHAPE_TEXTURE_WIDTH;
			pTextureTableData->height = SHAPE_TEXTURE_HEIGHT;
			pTextureTableData->size = TEXTURE_TABLE_BINARY_SIZE;
			pTextureTableData->count = TEXTURE_COUNT;

			lct::imag::TextureResource* pTextureResourceArray = pAllocator->AllocTypeArray<lct::imag::TextureResource>(TEXTURE_COUNT);
			memset(pTextureResourceArray, 0, (sizeof(lct::imag::TextureResource) * TEXTURE_COUNT));

			lct::imag::TextureTableAsset* pTextureTableAsset = pTextureTableAssetArray + textureTableIndex++;
			pTextureTableAsset->pTextureTableData = pTextureTableData;
			pTextureTableAsset->pTextureTableBinary = pTextureTableBinary;
			pTextureTableAsset->pTextureResourceArray = pTextureResourceArray;
		}
		{
			const u32 TEXTURE_COUNT = 1;
			const u32 TEXTURE_TABLE_BINARY_SIZE = JOINT_TEXTURE_SIZE * TEXTURE_COUNT;
			void* pTextureTableBinary = pAllocator->Alloc(TEXTURE_TABLE_BINARY_SIZE, 4);
			void* pMemory = pTextureTableBinary;
			{
				pMemory = WriteSquareTexture(JOINT_TEXTURE_WIDTH, JOINT_TEXTURE_HEIGHT, pMemory);
			}
			LCT_ASSERT((u32)pMemory == ((u32)pTextureTableBinary + TEXTURE_TABLE_BINARY_SIZE));

			lct::imag::TextureTableData* pTextureTableData = pAllocator->AllocType<lct::imag::TextureTableData>();
			strcpy(pTextureTableData->name, "joint");
			pTextureTableData->width = JOINT_TEXTURE_WIDTH;
			pTextureTableData->height = JOINT_TEXTURE_HEIGHT;
			pTextureTableData->size = TEXTURE_TABLE_BINARY_SIZE;
			pTextureTableData->count = TEXTURE_COUNT;

			lct::imag::TextureResource* pTextureResourceArray = pAllocator->AllocTypeArray<lct::imag::TextureResource>(TEXTURE_COUNT);
			memset(pTextureResourceArray, 0, (sizeof(lct::imag::TextureResource) * TEXTURE_COUNT));

			lct::imag::TextureTableAsset* pTextureTableAsset = pTextureTableAssetArray + textureTableIndex++;
			pTextureTableAsset->pTextureTableData = pTextureTableData;
			pTextureTableAsset->pTextureTableBinary = pTextureTableBinary;
			pTextureTableAsset->pTextureResourceArray = pTextureResourceArray;
		}
	}

	const u32 STRIP_COUNT = 3;

	lct::spri::StripData* pStripDataArray = pAllocator->AllocTypeArray<lct::spri::StripData>(STRIP_COUNT);
	{
		u32 stripIndex = 0;
		// shapes
		{
			lct::spri::StripData* pStripData = pStripDataArray + stripIndex++;
			strcpy(pStripData->patternName, "shape_inner");
			strcpy(pStripData->textureTableName, "shape");
		}
		{
			lct::spri::StripData* pStripData = pStripDataArray + stripIndex++;
			strcpy(pStripData->patternName, "shape_outer");
			strcpy(pStripData->textureTableName, "shape");
		}
		// joints
		{
			lct::spri::StripData* pStripData = pStripDataArray + stripIndex++;
			strcpy(pStripData->patternName, "joint");
			strcpy(pStripData->textureTableName, "joint");
		}
		LCT_ASSERT(stripIndex == STRIP_COUNT);
	}

	lct::spri::SkinAsset::Strip* pStripArray = pAllocator->AllocTypeArray<lct::spri::SkinAsset::Strip>(STRIP_COUNT);
	for (u32 stripIndex = 0; stripIndex < STRIP_COUNT; ++stripIndex) {
		lct::spri::SkinAsset::Strip* pStrip = pStripArray + stripIndex;

		lct::spri::StripData* pStripData = pStripDataArray + stripIndex;
		pStrip->pStripData = pStripData;

		lct::imag::TextureTableAsset* pMatchTextureTableAsset = NULL;
		for (u32 textureTableIndex = 0; textureTableIndex < TEXTURE_TABLE_COUNT; ++textureTableIndex)
		{
			lct::imag::TextureTableAsset* pTextureTableAsset = pTextureTableAssetArray + textureTableIndex;
			if (strcmp(pStripData->textureTableName, pTextureTableAsset->pTextureTableData->name) == 0)
			{
				pMatchTextureTableAsset = pTextureTableAsset;
				break;
			}
		}
		pStrip->pTextureTableAsset = pMatchTextureTableAsset;
	}

	lct::spri::SkinData* pSkinData = pAllocator->AllocType<lct::spri::SkinData>();
	strcpy(pSkinData->name, "test");
	pSkinData->stripCount = STRIP_COUNT;

	lct::spri::SkinAsset* pSkinAsset = pAllocator->AllocType<lct::spri::SkinAsset>();
	pSkinAsset->pSkinData = pSkinData;
	pSkinAsset->pStripArray = pStripArray;

	return pSkinAsset;
}

void* HardcodedSkin::WriteCircleTexture(u32 width, u32 height, u32 radius, void* pMemory)
{
	lct::imag::TexelRGBAData* pTexelDataArray = static_cast<lct::imag::TexelRGBAData*>(pMemory);
	lct::imag::TexelRGBAData* pTexelData = pTexelDataArray;

	f32 radiusSquared = (f32)radius * (f32)radius;
	f32 centerX = (f32)width / 2.0f;
	f32 centerY = (f32)height / 2.0f;
	f32 pointY = 0.5f;
	for (u32 texelY = 0; texelY < height; ++texelY)
	{
		f32 pointX = 0.5f;
		for (u32 texelX = 0; texelX < width; ++texelX)
		{
			f32 deltaX = pointX - centerX;
			f32 deltaY = pointY - centerY;
			f32 distanceSquared = deltaX * deltaX + deltaY * deltaY;
			if (distanceSquared <= radiusSquared)
			{
				pTexelData->r = 255;
				pTexelData->g = 255;
				pTexelData->b = 255;
				pTexelData->a = 255;
			}
			else
			{
				pTexelData->r = 0;
				pTexelData->g = 0;
				pTexelData->b = 0;
				pTexelData->a = 0;
			}
			++pTexelData;

			pointX += 1.0f;
		}
		pointY += 1.0f;
	}
	return pTexelData;
}

void* HardcodedSkin::WriteTriangleTexture(u32 width, u32 height, void* pMemory)
{
	lct::imag::TexelRGBAData* pTexelDataArray = static_cast<lct::imag::TexelRGBAData*>(pMemory);
	lct::imag::TexelRGBAData* pTexelData = pTexelDataArray;

	f32 halfWidth = (f32)width / 2.0f;
	f32 intercept = (f32)height * 2.0f;
	f32 pointY = 0.0f;
	for (u32 texelY = 0; texelY < height; ++texelY)
	{
		f32 pointX = 0.0f;
		for (u32 texelX = 0; texelX < width; ++texelX)
		{
			f32 lineY;
			if (pointX < halfWidth)
			{
				lineY = 2.0f * pointX;
			}
			else
			{
				lineY = -2.0f * pointX + intercept;
			}
			bool solid = pointY <= lineY;

			if (solid)
			{
				pTexelData->r = 255;
				pTexelData->g = 255;
				pTexelData->b = 255;
				pTexelData->a = 255;
			}
			else
			{
				pTexelData->r = 0;
				pTexelData->g = 0;
				pTexelData->b = 0;
				pTexelData->a = 0;
			}
			++pTexelData;

			pointX += 1.0f;
		}
		pointY += 1.0f;
	}
	return pTexelData;
}

void* HardcodedSkin::WriteSquareTexture(u32 width, u32 height, void* pMemory)
{
	lct::imag::TexelRGBAData* pTexelDataArray = static_cast<lct::imag::TexelRGBAData*>(pMemory);
	lct::imag::TexelRGBAData* pTexelData = pTexelDataArray;

	for (u32 texelY = 0; texelY < height; ++texelY)
	{
		for (u32 texelX = 0; texelX < width; ++texelX)
		{
			pTexelData->r = 255;
			pTexelData->g = 255;
			pTexelData->b = 255;
			pTexelData->a = 255;
			++pTexelData;
		}
	}
	return pTexelData;
}
